//*****************************************************************
/*
  NUSIC (Network Uncompressed Sound Intercommunicate Console): A Graphical User Interface for Jacktrip, a system for
  High-Quality Audio Network Performance over the Internet.

  http://code.google.com/p/nusic
  http://code.google.com/p/jacktrip

  Copyright (c) 2009 Elie El Noune
  Electrical and Computer Engineering
  American University of Beirut

  Permission is hereby granted, free of charge, to any person
  obtaining a copy of this software and associated documentation
  files (the "Software"), to deal in the Software without
  restriction, including without limitation the rights to use,
  copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the
  Software is furnished to do so, subject to the following
  conditions:

  The above copyright notice and this permission notice shall be
  included in all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  OTHER DEALINGS IN THE SOFTWARE.
*/
//*****************************************************************

#include <QtGui>

#include "window.h"


Window::Window(QWidget *parent)
    : QWidget(parent)
{
    //setup Jacktrip object
    SetJacktrip();
    QGridLayout *grid = new QGridLayout;

    //Mode
    CreateServer();
    CreateClient();
    CreateHostName();
    grid->addWidget(CreateModeGroup(), 0, 0, 1, 2);

    // ASIO options
    CreateSamplingRate();
    CreateBufferSize();
    CreateBitRes();
    grid->addWidget(CreateASIOOptionsGroup(), 1, 0, 1, 2);

    //More Options
    CreateLoopback();
    CreateQueueLength();
    CreateNumberChannels();
    grid->addWidget(CreateMoreOptionsGroup(), 2, 0, 1, 2);

    //Stream Options
    StartButton();
    StopButton();
    grid->addWidget(CreateStreamGroup(), 3, 0);

    //advanced options
    CreatePortOffset();
    CreateAdvanced();
    grid->addWidget(CreateAdvancedOptionsGroup(), 3, 1);

    //messages
    CreateMessageView();
    grid->addWidget(CreateMessageBox(),4,0,1,2);

    setLayout(grid);

    // Settings window
    QGridLayout *SettingsGrid = new QGridLayout;
    //Settings = new QWidget();
    Settings = new QDialog();
    Form = new QFormLayout(Settings);
    //Interface
    CreateJamlink();
    CreateJack();
    CreateRtaudio();
    SettingsGrid->addWidget(CreateInterfaceGroup(),0,0,1,2);

    //UDP Ports
    CreateBindPort();
    CreatePeerPort();
    SettingsGrid->addWidget(CreatePortGroup(),1,0,1,2);

    //Other
    CreateZeroUnderrun();
    CreateRedundancy();
    CreateClientName();

    SettingsGrid->addWidget(CreateOtherGroup(),2,0,1,2);
    SettingsGrid->addWidget(CreateClientGroup(),3,0,1,2);
    //Settings->setLayout(SettingsGrid);
    Form->addRow(CreateInterfaceGroup());
    Form->addRow(CreatePortGroup());
    Form->addRow(CreateOtherGroup());
    //Form->addRow("Redundancy Factor", RedundancyList);
   // Form->addRow("Underrun Mode", UnderrunList);
    Form->addRow("Client Name", ClientNameLine);
    Settings->setLayout(Form);
    Settings->setWindowIcon(QIcon("../icons/Settings.png"));
}

QGroupBox *Window::CreateModeGroup(){
    QGroupBox *mode = new QGroupBox(tr("Mode"));
    QHBoxLayout *vbox = new QHBoxLayout;
    vbox->addWidget(ServerButton);
    vbox->addWidget(ClientButton);
    QLabel *host = new QLabel(tr("Host Name or Address"));
    vbox->addWidget(host);
    vbox->addWidget(HostNameList);
    vbox->addStretch();
    mode->setLayout(vbox);

    return mode;
}

QGroupBox *Window::CreateASIOOptionsGroup(){
    ASIOOptions = new QGroupBox(tr("RtAudio Options"));
    QHBoxLayout *vbox = new QHBoxLayout;
    QLabel *srate = new QLabel(tr("Sampling rate in Hz"));
    vbox->addWidget(srate);
    vbox->addWidget(SamplingRateList);
    QLabel *bitres = new QLabel(tr("Bit Resolution"));
    vbox->addWidget(bitres);
    vbox->addWidget(BitResList);
    QLabel *bufsize = new QLabel(tr("Frames/Period"));
    vbox->addWidget(bufsize);
    vbox->addWidget(BufferSizeList);
    vbox->addStretch();
    ASIOOptions->setLayout(vbox);
    if (mJacktrip->isRtaudio() == false)
    ASIOOptions->setEnabled(false);
    return ASIOOptions;
}

QGroupBox *Window::CreateMoreOptionsGroup(){
    QGroupBox *MoreOptions = new QGroupBox(tr("More Options"));
    QHBoxLayout *vbox = new QHBoxLayout;
    vbox->addWidget(LoopbackBox);
    QLabel *channels = new QLabel(tr("Number of Channels"));
    vbox->addWidget(channels);
    vbox->addWidget(NumberChannelsLine);
    QLabel *queue = new QLabel(tr("Queue Size in Samples"));
    vbox->addWidget(queue);
    vbox->addWidget(QueueLengthLine);
    vbox->addStretch();
    MoreOptions->setLayout(vbox);

    return MoreOptions;
}

QGroupBox *Window::CreateStreamGroup(){
    QGroupBox *Stream = new QGroupBox(tr("Stream"));
    QHBoxLayout *vbox = new QHBoxLayout;
    vbox->addWidget(Start);
    vbox->addWidget(Stop);
    vbox->addStretch();
    Stream->setLayout(vbox);
    return Stream;
}

QGroupBox *Window::CreateAdvancedOptionsGroup(){
    QGroupBox *AdvancedOptions = new QGroupBox(tr("Advanced Options"));
    QHBoxLayout *vbox = new QHBoxLayout;
    QLabel *port = new QLabel(tr("Port offset from 4464"));
    vbox->addWidget(port);
    vbox->addWidget(PortOffsetLine);
    vbox->addWidget(Advanced);
    vbox->addStretch();
    AdvancedOptions->setLayout(vbox);
    return AdvancedOptions;
}

QGroupBox *Window::CreateMessageBox(){
    QGroupBox *Msg = new QGroupBox(tr("Messages"));
    QVBoxLayout *vbox = new QVBoxLayout;
    vbox->addWidget(Messages);
    vbox->addStretch();
    Msg->setLayout(vbox);
    return Msg;
}

QGroupBox *Window::CreateInterfaceGroup(){
    QGroupBox *Interface = new QGroupBox(tr("External Interfaces"));
    QVBoxLayout *vbox = new QVBoxLayout;
    vbox->addWidget(JamlinkBox);
    vbox->addWidget(JackBox);
    vbox->addWidget(RtaudioBox);
    vbox->addStretch();
    Interface->setLayout(vbox);
    return Interface;
}

QGroupBox *Window::CreatePortGroup(){
    QGroupBox *Port = new QGroupBox(tr("UDP Ports"));
    QVBoxLayout *vbox = new QVBoxLayout;
    QLabel *bind = new QLabel(tr("Bind Port"));
    vbox->addWidget(bind);
    vbox->addWidget(BindPortLine);
    QLabel *peer = new QLabel(tr("Peer Port"));
    vbox->addWidget(peer);
    vbox->addWidget(PeerPortLine);
    vbox->addStretch();
    Port->setLayout(vbox);
    return Port;
    }

QGroupBox *Window::CreateOtherGroup(){
    QGroupBox *Other = new QGroupBox(tr("Other"));
    QVBoxLayout *vbox = new QVBoxLayout;
    QLabel *redundancy = new QLabel(tr("Redundancy"));
    vbox->addWidget(redundancy);
    vbox->addWidget(RedundancyList);
    QLabel *under = new QLabel(tr("Underrun Mode"));
    vbox->addWidget(under);
    vbox->addWidget(UnderrunList);
    vbox->addStretch();
    Other->setLayout(vbox);
    return Other;
}

QGroupBox *Window::CreateClientGroup(){
    QGroupBox *Other = new QGroupBox(tr(""));
    QHBoxLayout *vbox = new QHBoxLayout;
    QLabel *client = new QLabel(tr("Set Client Name to "));
    vbox->addWidget(client);
    vbox->addWidget(ClientNameLine);
    vbox->addStretch();
    Other->setLayout(vbox);
    return Other;
}
void Window::CreateServer(){
    ServerButton = new QRadioButton(tr("Server"));
    ServerButton->setChecked(mJacktrip->isServer());
    QObject::connect(ServerButton, SIGNAL(clicked()), this, SLOT(setMode()));
}

void Window::CreateClient(){
    ClientButton = new QRadioButton(tr("Client"));
    ClientButton->setChecked(!mJacktrip->isServer());
    QObject::connect(ClientButton, SIGNAL(clicked()), this, SLOT(setMode()));
}

void Window::CreateHostName(){
    HostNameList = new QLineEdit;
    QObject::connect(HostNameList, SIGNAL(editingFinished()), this, SLOT(setHost()));
}

//conforming to JACK sampling rates
//the rates available vary for each asio device but we're giving the user more flexibility
void Window::CreateSamplingRate(){
    SamplingRateList = new QComboBox;
    SamplingRateList->addItem(tr("22050"));
    SamplingRateList->addItem(tr("32000"));
    SamplingRateList->addItem(tr("44100"));
    SamplingRateList->addItem(tr("48000"));
    SamplingRateList->addItem(tr("88200"));
    SamplingRateList->addItem(tr("96000"));
    SamplingRateList->addItem(tr("192000"));
    SamplingRateList->setCurrentIndex(3);
    QObject::connect(SamplingRateList, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(setSampleRate()));
}

void Window::CreateBitRes(){
     BitResList = new QComboBox;
     BitResList->addItem(tr("8"));
     BitResList->addItem(tr("16"));
     BitResList->addItem(tr("24"));
     BitResList->addItem(tr("32"));
     BitResList->setCurrentIndex(1);
     QObject::connect(BitResList, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(setBitRes()));
 }


 void Window::CreateBufferSize(){
    BufferSizeList = new QComboBox;
    BufferSizeList->addItem(tr("32"));
    BufferSizeList->addItem(tr("64"));
    BufferSizeList->addItem(tr("128"));
    BufferSizeList->addItem(tr("256"));
    BufferSizeList->addItem(tr("512"));
    BufferSizeList->addItem(tr("1024"));
    BufferSizeList->addItem(tr("2048"));
    BufferSizeList->setCurrentIndex(2);
    QObject::connect(BufferSizeList, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(setBufferSize()));
}

    void Window::CreateLoopback(){
        LoopbackBox = new QCheckBox(tr("Loopback"));
        LoopbackBox->setCheckable(true);
        LoopbackBox->setChecked(false);
        QObject::connect(LoopbackBox, SIGNAL(clicked()), this, SLOT(setLoopback()));
    }

    void Window::CreateQueueLength(){
        QueueLengthLine  = new QLineEdit();
        QueueLengthLine->setText("4");
        QObject::connect(QueueLengthLine, SIGNAL(editingFinished()), this, SLOT(setQueueLength()));
    }

    void Window::CreateNumberChannels(){
        NumberChannelsLine  = new QLineEdit();
        NumberChannelsLine->setText("2");
        QObject::connect(NumberChannelsLine, SIGNAL(editingFinished()), this, SLOT(setNumberChannels()));
    }

    void Window::StartButton(){
        Start = new QPushButton(QIcon("../icons/play.png"),tr("Start"),this);
        Start->setAutoExclusive(true);
        QObject::connect(Start, SIGNAL(clicked()), this, SLOT(StartJacktrip()));
    }

    void Window::StopButton(){
        Stop = new QPushButton(tr("Stop"),this);
        Stop->setIcon(QIcon("../icons/stop.png"));
        Stop->setAutoExclusive(true);
        QObject::connect(Stop, SIGNAL(clicked()), this, SLOT(StopJacktrip()));
    }

    void Window::CreateAdvanced(){
        Advanced = new QPushButton(QIcon("../icons/Settings.png"),tr("Settings"), this);
        QObject::connect(Advanced, SIGNAL(clicked()), this, SLOT(showSettings()));
    }

    void Window::CreatePortOffset(){
        PortOffsetLine = new QLineEdit(this);
        PortOffsetLine->setText("0");
        QObject::connect(PortOffsetLine, SIGNAL(editingFinished()), this, SLOT(setPortOffset()));
    }

    void Window::CreateJamlink(){
        JamlinkBox = new QCheckBox(tr("Jamlink"));
        JamlinkBox ->setCheckable(true);
        JamlinkBox ->setChecked(false);
        QObject::connect(JamlinkBox, SIGNAL(clicked()), this, SLOT(setJamlink()));
    }

    void Window::CreateJack(){
        JackBox = new QCheckBox(tr("Jack Audio Connection kit"));
        JackBox->setCheckable(true);
        if (mJacktrip->isRtaudio() == false)
        JackBox ->setChecked(true);
        JackBox->setAutoExclusive(true);
        QObject::connect(JackBox, SIGNAL(clicked()), this, SLOT(setRtaudio()));
    }

    void Window::CreateRtaudio(){
        RtaudioBox= new QCheckBox(tr("Rt-Audio"));
        RtaudioBox->setCheckable(true);
        if (mJacktrip->isRtaudio() == true)
        RtaudioBox->setChecked(true);
        RtaudioBox->setAutoExclusive(true);
        QObject::connect(RtaudioBox, SIGNAL(clicked()), this, SLOT(setRtaudio()));
    }

    void Window::CreateZeroUnderrun(){
        UnderrunList = new QComboBox();
        UnderrunList->addItem(tr("Wavetable"));
        UnderrunList->addItem(tr("Zeros"));
        UnderrunList->setCurrentIndex(0);
        QObject::connect(UnderrunList, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(setUnderrunMode()));
    }

    void Window::CreateBindPort(){
        BindPortLine = new QLineEdit();
        BindPortLine->setText("4464");
        QObject::connect(BindPortLine, SIGNAL(editingFinished()), this, SLOT(setBindPort()));
    }

    void Window::CreatePeerPort(){
        PeerPortLine = new QLineEdit();
        PeerPortLine->setText("4464");
        QObject::connect(PeerPortLine, SIGNAL(editingFinished()), this, SLOT(setPeerPort()));
    }

    void Window::CreateRedundancy(){
        RedundancyList = new QComboBox();
        RedundancyList->addItem(tr("1"));
        RedundancyList->addItem(tr("2"));
        RedundancyList->addItem(tr("3"));
        RedundancyList->addItem(tr("4"));
        RedundancyList->addItem(tr("5"));
        RedundancyList->addItem(tr("6"));
        RedundancyList->addItem(tr("7"));
        RedundancyList->addItem(tr("8"));
        RedundancyList->setCurrentIndex(0);
        QObject::connect(UnderrunList, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(setRedundancy()));
    }

    void Window::CreateClientName(){
        ClientNameLine = new QLineEdit();
        ClientNameLine->setText("JackTrip");
        QObject::connect(ClientNameLine, SIGNAL(editingFinished()), this, SLOT(setClientName()));
    }

    void Window::CreateMessageView(){
        Messages = new QTextEdit(this);
    }

    void Window::SetJacktrip(){

    mJacktrip = new Jacktrip();
    QString program = "jacktrip";

    //the next part is to ensure portability
    //taken from qjackctl application in qjackctlMainForm.cpp
    QFileInfo fi(program);
        if (fi.isRelative()) {
#if defined(WIN32)
                const char chPathSep = ';';
                if (fi.suffix().isEmpty())
                        program += ".exe";
#else
                const char chPathSep = ':';
#endif
                const QString sPath = ::getenv("PATH");
                QStringList paths = sPath.split(chPathSep);
                QStringListIterator iter(paths);
                while (iter.hasNext()) {
                        const QString& sDirectory = iter.next();
                        fi.setFile(QDir(sDirectory), program);
                        if (fi.isExecutable()) {
                                program = fi.filePath();
                                break;
                        }
                }
        }
    mJacktrip->setCommand(program);
    const QString& dir = QFileInfo(program).dir().absolutePath();    
    mJacktrip->setWorkingDirectory(dir);

    //Channel communication with Jacktrip process

    mJacktrip->setProcessChannelMode(QProcess::MergedChannels);

    //connections

    QObject::connect(mJacktrip,
                        SIGNAL(readyReadStandardOutput()),
                        this,
                        SLOT(readStdout()));

    /*
    QObject::connect(mJacktrip,
                        SIGNAL(error(QProcess::ProcessError)),
                        this,
                        SLOT(readStdout()));
                        */

}

    void Window::StartJacktrip(){
        if (mJacktrip->StartJacktrip() == 0)
        DisplayCommand();
    }

    void Window::StopJacktrip(){
        mJacktrip->StopJacktrip();
        QString msg = GetTime();
        msg+= "Jacktrip was stopped with exit status ";
        if (mJacktrip->exitStatus()== QProcess::NormalExit)
            msg+= "Normal";
        else msg+= "Crashed";
        msg+= "\n";
        Messages->append(msg);
    }

    void Window::readStdout(){
        QString message = "";
        QStringList lines = QString(mJacktrip->readAllStandardOutput().constData()).split("\n");
        for (int i=0; i<lines.length(); i++)
        {
            message+= GetTime();
            message+= lines.operator [](i);
            message+= "\n";
        }
        Messages->setFont(QFont("Times New Roman", 11, -1, false));
        Messages->append(message);
              
    }

    void Window::setMode(){
        mJacktrip->setServer(ServerButton->isChecked());
    }
    void Window::setHost(){
        mJacktrip->setHostName(HostNameList->text());
    }
    void Window::setLoopback(){
        mJacktrip->setLoopback(LoopbackBox->isChecked());
    }

    void Window::setNumberChannels(){
        mJacktrip->setNumberChannels(NumberChannelsLine->text());
    }
    void Window::setQueueLength(){
        mJacktrip->setQueueLength(QueueLengthLine->text());
    }

    void Window::setSampleRate(){
        mJacktrip->setSampleRate(SamplingRateList->currentText());
    }
    void Window::setBufferSize(){
        mJacktrip->setBufferSize(BufferSizeList->currentText());
    }

    void Window::setPortOffset(){
        mJacktrip->setPortOffset(PortOffsetLine->text());
    }
    void Window::setBitRes(){
        mJacktrip->setBitRes(BitResList->currentText());
    }

    void Window::setJamlink(){
        mJacktrip->setJamlink(JamlinkBox->isChecked());
    }

    void Window::setRtaudio(){
        mJacktrip->setRtaudio(RtaudioBox->isChecked());
        if (!mJacktrip->isRtaudio())
        ASIOOptions->setEnabled(false);
            else
                ASIOOptions->setEnabled(true);
    }

    void Window::setBindPort(){
        mJacktrip->setBindPort(BindPortLine->text());
    }
    void Window::setPeerPort(){
        mJacktrip->setPeerPort(PeerPortLine->text());
    }
    void Window::setRedundancy(){
        mJacktrip->setRedundancy(RedundancyList->currentText());
    }
    void Window::setUnderrunMode(){
          mJacktrip->setUnderrunMode(UnderrunList->currentText());
      }

    void Window::setClientName(){
          mJacktrip->setClientName(ClientNameLine->text());
    }

    void Window::DisplayCommand(){
        QTime now;
        QString command = now.currentTime().toString("hh:mm:ss:zzz");
        command += "\t";        
        command += "jacktrip";
        for (int i=0; i<mJacktrip->arglist.length(); i++)
        {
            command +=" ";
            command += mJacktrip->arglist.operator [](i);
        }
        command += "\n";
        Messages->append(command);
    }

    QString Window::GetTime(){
        QTime now;
        QString command = now.currentTime().toString("hh:mm:ss:zzz");
        command += "\t";
        return command;
    }
